package com.du.dupload;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

public class FFmpegSplitter implements Runnable {
	/**
	 * Execution flag
	 */
	private boolean executing = false;

	public synchronized boolean isExecuting() {
		return executing;
	}

	private synchronized void setExecuting(boolean executing) {
		this.executing = executing;
	}

	/**
	 * Splitter cancelation flag
	 */
	private boolean canceled = false;

	private boolean finished = false;

	private boolean error = false;

	private String filename;

	public String getFilename() {
		return filename;
	}

	private File file;

	private long fullLen;

	public synchronized boolean isError() {
		return error;
	}

	private synchronized void setError(boolean error) {
		this.error = error;
		if (error == true)
			this.canceled = true;
	}

	private List<String> out;

	public synchronized List<String> getResult() {
		if (isFinished()) {
			return new ArrayList<String>(out);
		} else {
			return new ArrayList<String>();
		}
	}

	public synchronized boolean isCanceled() {
		return canceled;
	}

	private synchronized void setCanceled(boolean canceled) {
		this.canceled = canceled;
	}

	private synchronized void addOut(String el) {
		out.add(el);
	}

	public synchronized void cancel() {
		this.canceled = true;
	}

	private long partLength;

	private int parts;

	private int progress;

	public synchronized int getProgress() {
		return progress;
	}

	private synchronized void setProgress(int progress) {
		this.progress = progress;
	}

	public FFmpegSplitter(String filename) {
		super();
		this.filename = filename;
		this.out = new ArrayList<String>();
	}

	public synchronized void split() throws IllegalStateException {
		while (isExecuting() && isCanceled()) {
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (isExecuting()) {
			return;
		}
		setExecuting(true);
		setCanceled(false);
		setFinished(false);
		setError(false);
		this.out = new ArrayList<String>();
		Thread t = new Thread(this, "FFmpeg splitter thread");
		t.start();
	}

	public synchronized boolean isFinished() {
		return finished;
	}

	private synchronized void setFinished(boolean finished) {
		this.finished = finished;
	}

	private Map<String, String> getInfo() {
		// System.out.println("Execute Inquiry for file: " + filename);
		Map<String, String> fieldMap = new HashMap<String, String>();

		try {
			// Build the command line
			StringBuilder sb = new StringBuilder();
			sb.append("ffmpeg");
			sb.append(" -i ");
			sb.append("\"" + filename + "\"");

			// Execute the command
			System.out.println("Command line: " + sb);
			Process p = Runtime.getRuntime().exec(sb.toString());
			Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
				public void run() {
					p.destroyForcibly();
				}
			}));
			p.waitFor();
			// Read the response
			BufferedReader input = new BufferedReader(new InputStreamReader(
					p.getInputStream()));
			BufferedReader error = new BufferedReader(new InputStreamReader(
					p.getErrorStream()));

			// Parse the input stream
			String line = input.readLine();
			// System.out.println("ffmpeg execution of: " + filename);
			while (line != null) {
				// System.out.println("\t***" + line);
				line = input.readLine();
			}

			// Parse the error stream
			line = error.readLine();
			// System.out.println("Error Stream: " + filename);
			while (line != null) {
				// System.out.println("1");
				// Handle the line
				if (line.startsWith("ffmpeg version")) {
					String version = line.substring(15,
							line.indexOf(" Copyright", 15));
					fieldMap.put("version", version);
				} else if (line.indexOf("Duration:") != -1) {

					String duration = line.substring(
							line.indexOf("Duration: ") + 10,
							line.indexOf(", start:"));
					fieldMap.put("duration", duration);

					String bitrate = line
							.substring(line.indexOf("bitrate: ") + 9);
					fieldMap.put("bitrate", bitrate);
				}
				// System.out.println("\t***" + line);
				line = error.readLine();
			}
		} catch (Exception e) {
			setError(true);
		}

		// Debug: dump fields:
		System.out.println("Fields:");
		for (String field : fieldMap.keySet()) {
			System.out.println("\t" + field + " = " + fieldMap.get(field));
		}

		return fieldMap;
	}

	private long getSecs(String str) {
		// 00:50:19.33
		String[] spl = str.split(":");

		int hours = Integer.parseInt(spl[0]);
		int minutes = Integer.parseInt(spl[1]);
		String[] spl2 = spl[2].split("\\.");
		int seconds = Integer.parseInt(spl2[0]);

		return (hours * 3600 + minutes * 60 + seconds);
	}

	private String getStringTime(long secs) {
		TimeZone tz = TimeZone.getTimeZone("UTC");
		SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
		df.setTimeZone(tz);
		String time = df.format(new Date(secs * 1000));
		return time;
	}

	private void splitPart(int partNum, long partLen) {
		StringBuilder sb = new StringBuilder();
		String start = getStringTime((partNum - 1) * partLen);
		if(partNum==1){
			start = getStringTime(11);
		}
		String end = getStringTime(partLen);
		String onlyFname = file.getName();
		String[] spl = onlyFname.split("\\.");
		String ext = spl[spl.length - 1];
		String fileNameWithoutExt = onlyFname.substring(0, onlyFname.length()
				- ext.length() - 1);
		String partName = fileNameWithoutExt + "_part" + partNum + "." + ext;
		String fullNewName = AppConfig.getInstance().getOutDir() + partName;

		sb.append("ffmpeg");
		sb.append(" -i ");
		sb.append("\"" + filename + "\"");
		sb.append(" -y ");
		sb.append(" -vcodec copy ");
		sb.append(" -acodec copy ");
		sb.append(" -ss ");
		sb.append(start);
		sb.append(" -t ");
		sb.append(end + " ");
		sb.append("\"" + fullNewName + "\"");

		// Execute the command
		System.out.println("Command line: " + sb);
		try {
			final Process p = Runtime.getRuntime().exec(sb.toString());
			Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
				public void run() {
					p.destroyForcibly();
				}
			}));

			// Read the response
			BufferedReader input = new BufferedReader(new InputStreamReader(
					p.getInputStream()));
			BufferedReader error = new BufferedReader(new InputStreamReader(
					p.getErrorStream()));

			String line = error.readLine();
			while (line != null && !isCanceled()) {
				if (line.indexOf("time=") != -1) {
					String duration = line.substring(line.indexOf("time=") + 5,
							line.indexOf(" bit"));
					setProgress((int) ((100 * ((long) getSecs(duration) + (partNum - 1)
							* partLength) / fullLen)));
					System.out.println(getProgress());
				}
				// System.out.println("\t***" + line);
				line = error.readLine();
			}
			p.destroyForcibly();
		} catch (Exception e) {
			setError(true);
		}
		if (!isCanceled()) {
			addOut(fullNewName);
		}
		System.out.println("end part:" + partNum);
	}

	@Override
	public void run() {
		file = new File(filename);
		Map<String, String> info = getInfo();
		fullLen = getSecs(info.get("duration"));
		parts = (int) (file.length() / ((long) 1024 * 1024 * 940 * 2) + 1);
		System.out.println(parts);
		partLength = fullLen / parts;
		System.out.println(partLength);
		for (int i = 1; i <= parts; i++) {
			splitPart(i, partLength);
		}
		if (!isCanceled()) {
			setFinished(true);
		}
		setExecuting(false);
	}

}
